package rwt.server.railroad;

import java.util.*;
import java.util.concurrent.*;

import rwt.server.*;
import rwt.server.WebMessages.PutAvailableTrains;
import rwt.server.core.Config;
import rwt.server.railroad.Train.ThrottleStatus;

public class TrainManager
{
	private static boolean isCreated = false;
	
	public static TrainManager create()
	{
		if (isCreated)
			throw new RuntimeException("TrainManager has already been created.");
		isCreated = true;
		return new TrainManager();
	}
	
	private ConcurrentMap<Integer, Train> trainMap = new ConcurrentHashMap<>();
	private List<Integer> sortedTrainIds;
	
	private TrainManager()
	{
		reset();
	}
	
	public void reset()
	{
		trainMap.clear();
		for (int trainId : Config.trainIds)
		{
			trainMap.put(trainId, new Train(trainId));
		}
		sortedTrainIds = new ArrayList<>(Config.trainIds);
		Collections.sort(sortedTrainIds);
	}
	
	public Train getTrainById(int trainId)
	{
		if (!trainMap.containsKey(trainId))
			throw new RuntimeException("Train '" + trainId + "' does not exist.");
		return trainMap.get(trainId);
	}
	
	public Train getTrainByPhysicalSlot(int slot)
	{
		for (int trainId : sortedTrainIds)
		{
			Train t = trainMap.get(trainId);
			if (t.getPhysicalSlotNum() == slot)
				return t;
		}
		
		return null;
	}
	
	public int trainCount()
	{
		return trainMap.size();
	}
	
	public List<Integer> getSortedTrainIds()
	{
		return sortedTrainIds;
	}
	
	public PutAvailableTrains getPutAvailableTrains()
	{
		Map<Integer, Integer> trainStates = new LinkedHashMap<>(4);
		Map<Integer, String> trainOperators = new LinkedHashMap<>(4);
		
		// Iterates over the trains in the order they are specified in the config file
		for (int trainId : sortedTrainIds)
		{
			Train t = trainMap.get(trainId);
			ThrottleStatus throttleStatus = t.getThrottleStatus();
			int availabilityState = WebMessages.TRAIN_UNINITIALIZED;
			
			if (throttleStatus == ThrottleStatus.NONE)
			{
				if (t.isInitialized())
					availabilityState = WebMessages.TRAIN_AVAILABLE;
			}
			else if (throttleStatus == ThrottleStatus.BASIC_THROTTLE)
			{
				availabilityState = WebMessages.TRAIN_TAKEN_BASIC;
			}
			else if (throttleStatus == ThrottleStatus.EMERGENCY_THROTTLE)
			{
				availabilityState = WebMessages.TRAIN_TAKEN_DIRECT;
			}
			
			trainStates.put(trainId, availabilityState);
			trainOperators.put(trainId, t.getOperatorName());
		}
		
		return new PutAvailableTrains(trainStates, trainOperators);
	}
	
	public Collection<Train> getTrains()
	{
		return trainMap.values();
	}
	
	/**
	 * Accepts a virtual slot number and returns the physical address of the train registered in that slot.
	 * <p>
	 * When running against the simulator, physical address will be 1111, 2222, 3333, or 4444. When running
	 * against the train lab, the physical address is the number painted on the side of the model train.
	 */
	public int slotToTrainId(int slot)
	{
		for (Train train : trainMap.values())
		{
			if (train.getVirtualSlotNum() == slot)
				return train.getTrainId();
		}
		return 0;
	}
}
